using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using System.Xml;

using OWED.Network;
using OWED.Network.Messages;
using OWED.Network.Messages.General;
using OWED.Network.Messages.Reply;

namespace OWED.Network
{
	using Loop = EventWaitHandle;

	/// <summary>
	/// Override this class and override ConnectionLoop.
	/// ConnectionLoop will be called once for each connection.
	/// </summary>
	public abstract class Server
	{
		/// <summary>
		/// Used for synchronization.
		/// </summary>
		private class ConnectionHandle
		{
			EventWaitHandle my_wait;
			EventWaitHandle my_reply_wait;
			Queue<IProtocolItem> my_queue;

			Queue<IProtocolItem> my_replies;

			int my_handled;
			Dictionary<int, EventWaitHandle> my_handle_waits;

			public ConnectionHandle()
			{

				my_wait = new EventWaitHandle(false, 
					EventResetMode.AutoReset);
				my_reply_wait = new EventWaitHandle(false,
						EventResetMode.AutoReset);

				my_handle_waits = new Dictionary<int, EventWaitHandle>();
				my_handle_waits[0] =
					new EventWaitHandle(false, EventResetMode.ManualReset);
				my_handle_waits[0].Set();

				my_queue = new Queue<IProtocolItem>();
				my_replies = new Queue<IProtocolItem>();
				my_handled = 0;
			}
			public void WaitFor(int num)
			{
				if (my_handle_waits.ContainsKey(num))
					my_handle_waits[num].WaitOne();
				else {
					my_handle_waits[num] = new EventWaitHandle(false,
						EventResetMode.ManualReset);
					my_handle_waits[num].WaitOne();
				}
			}
			public void Handled() { 
				my_handled++;
				if (!(my_handle_waits.ContainsKey(my_handled))) {
					my_handle_waits[my_handled] =
						new EventWaitHandle(false,
						EventResetMode.ManualReset);
				}
				my_handle_waits[my_handled].Set();
			}

			public void AddReply(IProtocolItem msg)
			{
				lock (this)
					my_replies.Enqueue(msg);
			}
			public IProtocolItem PopReply()
			{
				lock (this)
					return my_replies.Dequeue();
			}

			public void AddMessage(IProtocolItem msg)
			{
				lock (this)
					my_queue.Enqueue(msg);
			}
			public IProtocolItem PopMessage()
			{
				lock (this)
					return my_queue.Dequeue();
			}
			public void SetMessage() 
			{
				my_wait.Set();
			}
			public void WaitMessage()
			{
				my_wait.WaitOne();
			}

			public void SetReply()
			{
				my_reply_wait.Set();
			}
			public void WaitReply()
			{
				my_reply_wait.WaitOne();
			}

		}


		ConnectionServer my_server;

		public Server(int port)
		{
			my_server = new ConnectionServer(port, Handler);
		}

		private XmlDocument Handler(XmlDocument doc, ref object connection,
									int number)
		{
			if (connection == null) {
				lock (this) {
					ConnectionHandle handle_t = new ConnectionHandle();
					connection = handle_t;
				}
				Thread t1 = new Thread(ConnectionLoop);
				t1.IsBackground = true;
				t1.Start(connection);
			} 
			ConnectionHandle handle = connection as ConnectionHandle;
			if (handle != null) {
				handle.WaitFor(number);
				handle.AddMessage(MessageGet.GetFromXml(doc));
				handle.SetMessage();
			}

			handle.WaitReply();
			handle.Handled();
			return handle.PopReply().ToXml();
		}

		/// <summary>
		/// Override this method.
		/// You will be guaranteed message order.
		/// 
		/// Use Wait to get messages and Reply when you finish.
		/// You must ALWAYS reply!
		/// </summary>
		/// <param name="me">An object to pass around, don't mess with it.</param>
		protected virtual void ConnectionLoop(object me)
		{

		}

		/// <summary>
		/// Waits for the next message.
		/// </summary>
		/// <returns>The next message.</returns>
		protected virtual IProtocolItem Wait(object me)
		{
			(me as ConnectionHandle).WaitMessage();
			return (me as ConnectionHandle).PopMessage();
		}

		/// <summary>
		/// Sends your reply and updates things so that you can
		/// process the next message.
		/// </summary>
		/// <param name="me">Passed into your ConnectionLoop method</param>
		/// <param name="reply">The reply to send</param>
		protected virtual void Reply(object me, IProtocolItem reply)
		{
			(me as ConnectionHandle).AddReply(reply);
			(me as ConnectionHandle).SetReply();
		}
		/// <summary>
		/// Ignore me.
		/// </summary>
		private void Close()
		{

		}

		/// <summary>
		/// Frees up the socket port when you're done
		/// </summary>
		public void Dispose()
		{
			my_server.Dispose();
		}
	}
}
